package net.blank.ui.core
{
	import net.blank.bitmapOperation.BitmapDraw;
	
	import net.blank.blankLoader.ResourceItem;
	import net.blank.blankLoader.SequenceLoader;
	
	import net.blank.dataTransformation.fileInfo.FileInfo;
	
	import flash.display.Bitmap;
	import flash.display.DisplayObject;
	import flash.display.LoaderInfo;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.EventDispatcher;
	import flash.geom.Rectangle;
	import flash.net.URLRequest;
	import flash.text.engine.ContentElement;
	import flash.text.engine.ElementFormat;
	import flash.text.engine.FontDescription;
	import flash.text.engine.FontLookup;
	import flash.text.engine.FontPosture;
	import flash.text.engine.FontWeight;
	import flash.text.engine.GraphicElement;
	import flash.text.engine.GroupElement;
	import flash.text.engine.TextElement;
	import flash.utils.Dictionary;
	
	import org.bytearray.gif.player.GIFPlayer;
	
	/**
	 * html标签转换器
	 * @author blank
	 * 
	 */
	public class HTMLConverter
	{
		/**
		 * 从 CSS 栈当中获取指定的属性值
		 * @param propertyName 属性名
		 * @param cssStack 栈列表
		 * @param len 栈深
		 * @return 属性值
		 * 
		 */
		private static function getProperty(propertyName:String,cssStack:Vector.<Object>,len:int):*{
			for(var i:int=len-1;i>-1;i--){
				var css:Object=cssStack[i];
				if(css is XML){
					if(String(css.@[propertyName])){
						return css.@[propertyName];
					}
				}else if(css.hasOwnProperty(propertyName)){
					return css[propertyName];
				}
			}
			return null;
		}
		
		/**
		 * 获得文本元素
		 * @param htmlXml
		 * @param cssStack
		 * @param len
		 * @param eventMirror
		 * @return 
		 * 
		 */
		private static function getTextElement(htmlXml:XML,cssStack:Vector.<Object>,len:int,eventMirror:EventDispatcher):TextElement{
			var te:TextElement=new TextElement(
				String(htmlXml),
				new ElementFormat(
					new FontDescription(
						getProperty("fontName",cssStack,len) || "_seri",
						getProperty("isBold",cssStack,len) ? FontWeight.BOLD : FontWeight.NORMAL,
						getProperty("isItalic",cssStack,len) ? FontPosture.ITALIC : FontPosture.NORMAL,
						getProperty("isEmbedded",cssStack,len) ? FontLookup.EMBEDDED_CFF : FontLookup.DEVICE
					),
					getProperty("fontSize",cssStack,len) || 12,
					getProperty("color",cssStack,len) || 0x000000,
					getProperty("alpha",cssStack,len) || 1,
					"auto","roman","useDominantBaseline",0,"on",
					getProperty("trackingRight",cssStack,len) || 0,
					getProperty("trackingLeft",cssStack,len) || 0
				)
			);
			
			var link:String=getProperty("event",cssStack,len);
			if(link && eventMirror){
				te.eventMirror=eventMirror;
				te.userData=link;
			}
			
			return te;
		}
		
		/**
		 * 获得图形元素
		 * @param bmpCvs
		 * @param width
		 * @param height
		 * @param cssStack
		 * @param len
		 * @param eventMirror
		 * @return 
		 * 
		 */
		private static function getGraphicElement(bmpCvs:DisplayObject,width:Number,height:Number,cssStack:Vector.<Object>,len:int,eventMirror:EventDispatcher):GraphicElement{
			var ge:GraphicElement=new GraphicElement(
				bmpCvs,width,height,
				new ElementFormat(
					null,
					12,
					0x000000,
					getProperty("alpha",cssStack,len) || 1,
					"auto","roman","useDominantBaseline",0,"on",
					getProperty("trackingRight",cssStack,len) || 0,
					getProperty("trackingLeft",cssStack,len) || 0
				)
			);
			
			var link:String=getProperty("event",cssStack,len);
			if(link && eventMirror){
				ge.eventMirror=eventMirror;
				ge.userData=link;
			}
			
			return ge;
		}
		
		/**
		 * img标签转换为图形元素
		 * @param htmlXml
		 * @param cssStack
		 * @param sequenceLoader
		 * @param urlInstanceCache
		 * @param eventMirror
		 * @return 
		 * 
		 */
		private static function img2GraphicElement(htmlXml:XML,cssStack:Vector.<Object>,sequenceLoader:SequenceLoader,urlInstanceCache:Dictionary,eventMirror:EventDispatcher):GraphicElement{
			var len:int=cssStack.length;
			
			var bmpUrl:String=String(htmlXml.@url) || getProperty("url",cssStack,len);
			if(bmpUrl){
				var bmpWidth:Number=Number(htmlXml.@width) || getProperty("width",cssStack,len);
				var bmpHeight:Number=Number(htmlXml.@height) || getProperty("height",cssStack,len);
				
				var bmpCvs:DisplayObject;
				if(FileInfo.getFileNameInfo(bmpUrl,FileInfo.INFO_TYPE_FILE_EXTENSION).toLocaleUpperCase() == "GIF"){
					var gifPlayer:GIFPlayer;
					if(urlInstanceCache && bmpUrl in urlInstanceCache){
						if(urlInstanceCache[bmpUrl] is Class){
							gifPlayer=new GIFPlayer(true);
							gifPlayer.loadBytes(new urlInstanceCache[bmpUrl]());
						}else{
							gifPlayer=urlInstanceCache[bmpUrl];
						}
					}else{
						gifPlayer=new GIFPlayer(true);
						gifPlayer.load(new URLRequest(bmpUrl));
					}
					
					bmpCvs=gifPlayer;
				}else{
					var shape:Shape=new Shape();
					
					if(urlInstanceCache && bmpUrl in urlInstanceCache){
						if(urlInstanceCache[bmpUrl] is Class){
							BitmapDraw.drawBitmap(shape.graphics,new urlInstanceCache[bmpUrl](),new Rectangle(0,0,bmpWidth,bmpHeight));
						}else if(urlInstanceCache[bmpUrl] is Bitmap){
							BitmapDraw.drawBitmap(shape.graphics,urlInstanceCache[bmpUrl].bitmapData,new Rectangle(0,0,bmpWidth,bmpHeight));
						}else{
							BitmapDraw.drawBitmap(shape.graphics,urlInstanceCache[bmpUrl],new Rectangle(0,0,bmpWidth,bmpHeight));
						}
					}else if(sequenceLoader){
						sequenceLoader.startLoad(
							new ResourceItem(
								ResourceItem.TYPE_BITMAP,
								bmpUrl,
								0,
								null,
								false,
								function(bmp:Bitmap):void{
									BitmapDraw.drawBitmap(shape.graphics,bmp.bitmapData,new Rectangle(0,0,bmpWidth,bmpHeight));
								}
							)
						);
					}else{
						throw new Error("缺少资源加载器或在缓存列表中未能查找到 "+bmpUrl);
					}
					
					bmpCvs=shape;
				}
				
				return getGraphicElement(bmpCvs,bmpWidth,bmpHeight,cssStack,len,eventMirror);
			}
			return null;
		}
		
		/**
		 * swf转换为图形元素
		 * @param htmlXml
		 * @param cssStack
		 * @param sequenceLoader
		 * @param urlInstanceCache
		 * @param eventMirror
		 * @return 
		 * 
		 */
		private static function swf2GraphicElement(htmlXml:XML,cssStack:Vector.<Object>,sequenceLoader:SequenceLoader,urlInstanceCache:Dictionary,eventMirror:EventDispatcher):GraphicElement{
			var len:int=cssStack.length;
			
			var swfUrl:String=String(htmlXml.@url) || getProperty("url",cssStack,len);
			if(swfUrl){
				var swfWidth:Number=Number(htmlXml.@width) || getProperty("width",cssStack,len);
				var swfHeight:Number=Number(htmlXml.@height) || getProperty("height",cssStack,len);
				
				if(urlInstanceCache && swfUrl in urlInstanceCache){
					var mc:DisplayObject;
					if(urlInstanceCache[swfUrl] is Class){
						mc=new urlInstanceCache[swfUrl]();
					}else{
						mc=urlInstanceCache[swfUrl];
					}
					
					mc.width=swfWidth;
					mc.height=swfHeight;
					
					return getGraphicElement(mc,swfWidth,swfHeight,cssStack,len,eventMirror);
					
				}else if(sequenceLoader){
					var container:Sprite=new Sprite();
					
					sequenceLoader.startLoad(
						new ResourceItem(
							ResourceItem.TYPE_DLL,
							swfUrl,
							0,
							null,
							false,
							function(l:LoaderInfo):void{
								container.addChild(l.content);
							}
						)
					);
					
					return getGraphicElement(container,swfWidth,swfHeight,cssStack,len,eventMirror);
				}else{
					throw new Error("缺少资源加载器或在缓存列表中未能查找到 "+swfUrl);
				}
			}
			return null;
		}
		
		/**
		 * 添加元素到最后一个段落中
		 * @param element
		 * @param paragraphs
		 * 
		 */
		private static function addElementToParagraphs(element:ContentElement,paragraphs:Vector.<ContentElement>):void{
			var len:int=paragraphs.length;
			
			var g:GroupElement=paragraphs[len-1] as GroupElement;
			g.replaceElements(g.elementCount,g.elementCount,Vector.<ContentElement>([element]));
		}
		
		/**
		 * 将 HTML 转换为 ContentElement 列表
		 * @param htmlXml 由 HTML 转换成的 XML
		 * @param csses CSS样式列表
		 * @param cssStack CSS样式栈
		 * @param paragraphs 转换后的 段落 列表
		 * @param sequenceLoader 序列化加载器,用于加载 url 
		 * @param urlInstanceCache url 实例或 Class 缓存器
		 * @param eventMirror 超链接事件派发器
		 * 
		 */
		private static function html2ContentElement(
			htmlXml:XML,
			csses:Dictionary,
			cssStack:Vector.<Object>,
			paragraphs:Vector.<ContentElement>,
			sequenceLoader:SequenceLoader=null,
			urlInstanceCache:Dictionary=null,
			eventMirror:EventDispatcher=null
		):void{
			var len:int;
			
			var name:String=htmlXml.name();
			if(!name){
				len=cssStack.length;
				
				addElementToParagraphs(getTextElement(htmlXml,cssStack,len,eventMirror),paragraphs);
			}
			
			if(name == "br"){
				paragraphs.push(new GroupElement());
			}else{
				var clsName:String, css:Object, mySelfCss:Object;
				
				clsName=String(htmlXml.@["class"]);
				if(clsName in csses){
					css=csses[clsName];
					cssStack.push(css);
				}
				
				cssStack.push(htmlXml);
				
				switch(name){
					case "p":
						if((paragraphs[paragraphs.length-1] as GroupElement).elementCount > 0){
							paragraphs.push(new GroupElement());
						}
					case "html":
					case "a":
					case "span":{
						var children:XMLList=htmlXml.children();
						for each(var child:XML in children){
							html2ContentElement(child,csses,cssStack,paragraphs,sequenceLoader,urlInstanceCache,eventMirror);
						}
					}break;
					case "img":{
						addElementToParagraphs(img2GraphicElement(htmlXml,cssStack,sequenceLoader,urlInstanceCache,eventMirror),paragraphs);
					}break;
					case "swf":{
						addElementToParagraphs(swf2GraphicElement(htmlXml,cssStack,sequenceLoader,urlInstanceCache,eventMirror),paragraphs);
					}break;
				}
				
				cssStack.pop();
				if(css){
					cssStack.pop();
				}
			}
		}
		
		/**
		 * 将 HTML 转换为 GroupElement 列表
		 * @param htmlXml 由 HTML 转换成的 XML
		 * @param csses CSS样式列表
		 * @param sequenceLoader 序列化加载器,用于加载 url 
		 * @param urlInstanceCache url 实例或 Class 缓存器
		 * @param eventMirror 超链接事件派发器
		 * @return 
		 * 
		 */
		public static function convert(htmlXml:XML,csses:Dictionary=null,sequenceLoader:SequenceLoader=null,urlInstanceCache:Dictionary=null,eventMirror:EventDispatcher=null):Vector.<ContentElement>{
			var paragraphs:Vector.<ContentElement>=Vector.<ContentElement>([new GroupElement()]);
			
			html2ContentElement(htmlXml,csses || new Dictionary(),new Vector.<Object>(),paragraphs,sequenceLoader,urlInstanceCache,eventMirror);
			
			return paragraphs;
		}
	}
}